#ifndef __SINK_H__
#define __SINK_H__

#include "util.hpp"
#include <iostream>
#include <fstream>
#include <memory>
#include <cassert>
#include <sstream>

namespace mylog{
  class LogSink{
      public:
        using ptr = std::shared_ptr<LogSink>;
        LogSink() {}
        virtual ~LogSink() {}
        virtual void log(const char *data, size_t len) = 0;
  };
  //落地方向：标准输出
  class StdoutSink : public LogSink{
    public:
      //将日志消息写到标准输出
      void log(const char *data, size_t len){
        std::cout.write(data,len);
      }
  };

  class FileSink : public LogSink{
     public:
       FileSink(const std::string &pathname):_pathname(pathname){
           //1.创建日志文件所在目录
         mylog::Util::File::CreateDirectory(mylog::Util::File::Path(pathname)); 
           //2.创建并打开对应的文件
         _ofs.open(_pathname, std::ios::binary | std::ios::app);
         assert(_ofs.is_open());
       }
       //将日志消息写到文件中
       void log(const char *data, size_t len){
         _ofs.write((const char*) data, len);
         assert(_ofs.good());  //假如文件打开失败，直接报错
       }

     private:
       std::string _pathname;
       std::ofstream _ofs;
  };


  class RollFileSink : public LogSink{
     public:
       RollFileSink(const std::string &basename, size_t max_size):
      _basename(basename),_max_size(max_size),_cur_size(0)
    {
         std::string filename = createFile(); //创建对应的文件名
         mylog::Util::File::CreateDirectory(mylog::Util::File::Path(filename));
         _ofs.open(filename, std::ios::binary | std::ios::app);
         assert(_ofs.is_open());
    }
       //将日志消息写到文件中
       void log(const char *data, size_t len){
         if(_cur_size >= _max_size){
           _ofs.close();  //关闭源文件
           std::string filename = createFile();
           _ofs.open(filename, std::ios::binary | std::ios::app);
           assert(_ofs.is_open());
           _cur_size = 0;   //新文件大小为0
         }
         _ofs.write((const char*)data, len);
         assert(_ofs.good());
         _cur_size += len;  //写完数据，调整当前文件大小
       }
     private:
       //判断当前文件是否满，然后创建新的文件
       std::string createFile(){
         //获取系统时间，以时间来构造文件
         time_t t = mylog::Util::Date::getTime();
         struct tm lt;
         localtime_r(&t, &lt);
         std::stringstream filename;
         filename << _basename;
         filename << lt.tm_year + 1900;
         filename << lt.tm_mon + 1;
         filename << lt.tm_mday;
         filename << lt.tm_hour;
         filename << lt.tm_min;
         filename << lt.tm_sec;
         filename << "-";
         filename << _name_count++;
         filename << ".log";
         return filename.str();
       }
     private:
       size_t _name_count;  //可能1s创建n个文件，导致文件名相同，因此再加一个计数值做区分
       std::string _basename;
       std::ofstream _ofs;
       size_t _max_size;  //文件所能存储最大的大小
       size_t _cur_size;  //文件当前所存储的大小
  };
  
  //由于不同类所需参数不同，因此使用参数包
  class SinkFactory{
     public:
       template <typename SinkType, typename ...Args>
       static LogSink::ptr create(Args &&...args){
         return std::make_shared<SinkType>(std::forward<Args>(args)...);
       }
  };
}

#endif
